package app.server.project.core.tcp;

import app.server.project.core.constants.CfgDef;
import app.server.project.core.constants.Def;
import app.server.project.core.msgdispatcher.Dispatcher;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.CharsetUtil;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class TcpServer {

    private final static int READ_TIME_OUT = 30;
    private final static int WRITE_TIME_OUT = 30;
    private final static int BODY_LEN = 2;

    @Value("${" + CfgDef.tcpPort + ":9988}")
    private int port;
    @Value("${" + CfgDef.backLog + ":" + Short.MAX_VALUE + "}")
    private int backLog;
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;
    @Autowired
    private TaskExecutor taskScheduler;
    @Autowired
    private TcpSessionMgr tcpSessionMgr;
    @Autowired
    private Dispatcher dispatcher;
    @Autowired
    private ApplicationContext applicationContext;

    @PostConstruct
    public void bind() throws InterruptedException {
        this.taskScheduler.execute(() -> {
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
                ServerBootstrap bootstrap = new ServerBootstrap();
                bootstrap.group(bossGroup, workerGroup)
                        .channel(NioServerSocketChannel.class)
                        .option(ChannelOption.SO_BACKLOG, backLog)
                        .handler(new LoggingHandler(LogLevel.INFO))
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
                                //空闲检查,写超时,将会，flush数据,读超时,将会断掉连接
                                ch.pipeline().addLast(new IdleStateHandler(READ_TIME_OUT, Def.Zero, Def.Zero, TimeUnit.SECONDS));
                                // 解码逻辑
                                ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Short.MAX_VALUE, Def.Zero, BODY_LEN, Def.Zero, BODY_LEN));
                                ch.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));
                                ch.pipeline().addLast(new JsonDecoder());
                                // 编码逻辑
                                ch.pipeline().addLast(new LengthFieldPrepender(BODY_LEN));
                                ch.pipeline().addLast(new  JsonEncoder());
                                // 处理客户端的请求的数据，并且进行响应
                                ch.pipeline().addLast(new SessionHandler(tcpSessionMgr,dispatcher));

                            }
                        });
                log.info("=========tcp 服务器启动,端口:{}", this.port);
                ChannelFuture future = bootstrap.bind(port).sync();
                future.channel().closeFuture().sync();

            } catch (Exception e) {
                //log.error("tcp服务器启动失败", e);
            } finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        });

    }
}
